﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using SteamFluentAPI.Infrastructure;
using SteamFluentAPI.Infrastructure.Base;

namespace SteamFluentAPI.Builder
{
    /// <summary>
    /// Handles the construction of the SteamRequest interface and also filters the list of methods available.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SteamInterfaceBuilder<T> where T : SteamInterface
    {
        private SteamRequest request;

        /// <summary>
        /// Begins construction of the interface.
        /// </summary>
        /// <param name="request">The SteamRequest being passed on.</param>
        public SteamInterfaceBuilder(SteamRequest request)
        {
            this.request = request;
        }

        /// <summary>
        /// Begins construction of the interface.
        /// </summary>
        /// <param name="steamGameType">The SteamGameType of this interface.</param>
        /// <returns></returns>
        public SteamInterfaceBuilder<T> GameType(SteamGameType steamGameType)
        {
            request.SteamGameType = steamGameType;
            return this;
        }

        /// <summary>
        /// Select a method from a collection of methods belonging to the chosen interface
        /// </summary>
        /// <typeparam name="M"></typeparam>
        /// <param name="method">x => x.(method name)</param>
        /// <returns></returns>
        public SteamMethodBuilder<M> Method<M>(Expression<Func<T, M>> method) where M : SteamMethod
        {
            return new SteamMethodBuilder<M>(loadSteamRequest(method));
        }

        private SteamRequest loadSteamRequest<M>(Expression<Func<T, M>> method) where M : SteamMethod
        {
            try
            {
                PropertyInfo info = (method.Body as MemberExpression).Member as PropertyInfo;
                
                if (!info.IsImplemented())
                {
                    throw new ApplicationException(info.Name + " has not been implemented.");
                }

                SteamMethodAttribute methodAtt = null;

                if (info.IsSteamMethod(out methodAtt))
                {
                    request.MethodName = !string.IsNullOrEmpty(methodAtt.MethodName) ? methodAtt.MethodName : info.Name;

                    if (string.IsNullOrEmpty(methodAtt.VersionCode))
                    {
                        throw new ApplicationException(info.Name + " requires a version code.");
                    }

                    request.VersionCode = methodAtt.VersionCode;
                }
                else
                {
                    throw new ApplicationException(info.Name + " is not a valid method.");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return request;
        }

        #region Hiding Methods

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Boolean Equals(Object obj)
        {
            return base.Equals(obj);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Int32 GetHashCode()
        {
            return base.GetHashCode();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override String ToString()
        {
            return base.ToString();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Type GetType()
        {
            return base.GetType();
        }

        #endregion
    }
}
